作者:陈广 日期:2019-1-16
ID 卡全称为身份识别卡(Identification Card),是一种最简单的 RFID 卡。它不可写入,含固定的编号,主要有台湾 SYRIS 的 EM 格式、美国 HIDMOTOROLA 等各类 ID 卡。ID 卡与磁卡一样,都仅仅使用了“卡的号码”而已,卡内除了卡号外,无任何保密功能,其“卡号”是公开、裸露的。所以说 ID 卡就是“感应式磁卡”。 ISO 标准 ID 卡的规格为: 85.6x54x0.80±0.04mm(高/宽/厚),市场上也存在一些厚、薄卡或异型卡。
ID 卡系统由卡、读卡器和后台控制器组成。工作过程如下:
以上描述过于专业,我们讲通俗一点:ID 卡本身是没有电池的,在不接触的情况下读卡器如何读取卡内存储的信息呢?其实我们在中学学物理时都知道,当电磁波通过线圈时会使线圈产生电流。ID 卡内就有线圈,我们将 ID 卡对着灯光看就能看见线圈。而读卡器则发送电磁波,当 ID 卡靠近读卡器时,线圈产生电流,这样 ID 卡就有了电,然后 ID 卡通过这点电将卡的信息通过卡中的天线发送给读卡器,这样读卡器就可以读取卡中的信息了。
ID 卡的使用非常普遍,我用到的就有:住宅楼楼下铁门的门禁卡,电单车出入卡(准备被人脸识别取代)。另外还有停车场使用它来进行身份识别(现在也基本被摄像头 AI 识别所取代)。由于其无须内置电源,使用时无接触且寿命长,因此在弱电系统中有广泛的应用。
ID卡的出现基本上淘汰了早期的磁卡或接触式 IC 卡。但由于 ID 卡不可写入用户数据,其记录内容仅限卡号只可由芯片厂一次性写入,开发商只可读出卡号加以利用。随着市场技术不断的发展,现在市场上到处都可以买到 ID 门禁卡复制机,ID 门禁卡是可以进行复制的,这在安全问题上带来了一定为威胁。相比于 ID 卡,IC 卡的安全性更高,像公交卡,里面还存有卡号、用户资料、使用权限、卡上金额等,数据的读取、写入均需要相应的密码认证,是一种双向认证的卡,相对而言,难以复制。所以业界共识是 ID 卡不可能做成一卡通,也不可能做消费,除非能接受其不足之处(如上所述)。
我在系里找到两种类型的 125KHz 读卡器,一种是实验箱里的,裸体读卡器,下篇文章再针对它进行介绍。另一种是单独的 125KHz 读卡器,可读取 ID 卡和 IC 卡(改天看看有没有 IC 卡,找一张来读读),如下图:
此读卡器应当是实验箱配的,估计也是厂家从淘宝买的,居然把读卡器后面的参数说明标签全部撕掉。要使用只能自己尝试了。另外不同的读卡器读卡时输出的格式是不一样的,万幸的是我在淘宝搜到以下说明。弄懂了在读取卡中数据后如何进行转换以得到正确的 ID 卡号码。
ID卡的原始卡号是固定的,而读卡器根据原始卡号可以转换输出不同的位数和格式,各个厂家的读卡器输出不太一样,但基本上是按一定的标准输出,以下是各个厂家不同读卡器的数据输出格式。
此读卡器采用 UART 接口与计算机通信,UART 接口一帧的数据模式为 1 个起始位,8个数据位,无奇偶校验位,波特率为 9600 bps(刚开始我用 19200 试了半天,结果老读不出正确的 ID 卡号),其向外输出的数据格式参考下表:
卡号 | 校验 |
---|---|
4 字节 | 1 字节 |
当有卡进入读卡器的射频区域时,读卡器通过 UART 接口发送上表所示格式的数据包,即 5 个字节,如果卡一直处于射频区域内,读卡器不再重复发送。卡片离开射频区域,也不会再发送任何数据。
以图2中的上边那张 ID 卡为例,卡上标的号码为:0003322327 050,45527
。
读此卡时,读卡器发送的数据为:00 32 B1 D7 54
。
下面我们讲解如何进行转换。
参照上面的格式2,将前面 4 个字节00 32 B1 D7
当成一个十六进制数字变为数字:0x32B1D7
,转换为十进制数字为:3322327
,前面补0变为10位数字则正好是卡上标的第一个号码0003322327
。
参照上面的格式4,将倒数5、6位数字32
转换为 3 位十进制数为:050
;再将后4位数字B1D7
转换为 5 位十进制变成:45527
,在计算出的两个数字间加上逗号变成050,45527
,正好对应卡片上标注的第二个号码。
编程时碰到一些困难,在第一次读卡时,可以一次性收到 5 个字节,但第二次刷卡时就要分几次才能收到完整数据。惟一确定的是收到的数据长度为5,只能通过这点来保证正确读取卡号了。另外上网也没搜到验证码的算法是什么。没办法,只能直接使用前四个字节的卡号而不验证了。
另外前面我们在讲解《UART 数据通信格式》时说过数据从最低位开始传输(小端传输),此时接收到的前四个数据为00 32 B1 D7
。而通过BitConverter.ToUInt32
方法将其转化为 32 位无符号整数所得到的结果是3618714112
,即十六进制的D7 B1 32 00
。这跟接收到的字节正好颠倒,需要将转化出来的整数进行反转操作。即将4个字节的整数的第1个和第4个字节调换,第2和第3个字节调换。
新建一个控制台应用程序,使用如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Ports;
namespace ConsoleApp2
{
class Program
{
static SerialPort sp = new SerialPort();
static byte[] cardNum = new byte[5];
static int index = 0;
static void Main(string[] args)
{
sp.PortName = "COM3";
sp.BaudRate = 9600;
sp.Open();
sp.DataReceived += Sp_DataReceived;
Console.ReadLine();
}
static void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (sp.IsOpen)
{
byte[] receivedData = new byte[sp.BytesToRead];
sp.Read(receivedData, 0, receivedData.Length);
foreach (var num in receivedData)
{
cardNum[index++] = num;
if (index == 5)
{
index = 0;
PrintCardNo(); //打印 ID 卡信息
}
}
}
}
static void PrintCardNo()
{
//打印10位卡号码
UInt32 cardNo = BitConverter.ToUInt32(cardNum, 0);
Console.Write(ReverseBytes(cardNo).ToString().PadLeft(10, '0') + " ");
//打印8位卡号码的第一部分
Console.Write(cardNum[1].ToString().PadLeft(3, '0') + ",");
//打印8位卡号码的第二部分
UInt16 part2 = BitConverter.ToUInt16(cardNum, 2);
Console.WriteLine(ReverseBytes(part2).ToString().PadLeft(5, '0'));
}
//翻转32位整数字节序
public static UInt32 ReverseBytes(UInt32 value)
{
return (value & 0x000000FFu) << 24 | (value & 0x0000FF00u) << 8 |
(value & 0x00FF0000u) >> 8 | (value & 0xFF000000u) >> 24;
}
//翻转16位整数字节序
public static UInt16 ReverseBytes(UInt16 value)
{
return (UInt16)((value & 0x00FFu) << 8 | (value & 0xFF00u) >> 8);
}
}
}
运行程序,我用了2张卡轮流刷,得到如下结果:
0003322327 050,45527
0002941942 044,58358
0003322327 050,45527
0003322327 050,45527
0002941942 044,58358
0003322327 050,45527
注意:运行本程序之前请注意在【设备管理器】中查看读卡器所使用的端口号是否是 COM3 ,如不是,请更改代码为适合的端口号。
对比程序运行结果与图 2 中的 ID 卡表面标注编号,完全相符。
;